Skip to content

[tests] Cover interface method desugaring parity - #12610

Open
simonrozsival wants to merge 6 commits into
mainfrom
simonrozsival-interface-desugaring-parity
Open

[tests] Cover interface method desugaring parity#12610
simonrozsival wants to merge 6 commits into
mainfrom
simonrozsival-interface-desugaring-parity

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Summary

  • replace the stale NativeAOT SupportDesugaringStaticInterfaceMethods skip with deterministic device coverage
  • exercise generated static/default methods, nested interfaces, managed construction, a concrete default override, and a covariant Java bridge
  • verify API-native and forced pre-API 24 companion-class DEX forms under D8 and R8
  • compare llvm-ir/CoreCLR, trimmable/CoreCLR, and trimmable/NativeAOT without changing production code

Part of #12561.

Investigation

The NativeAOT skip was added by 847c5fe16 in March 2026. Trimmable static-method fallback support landed later in daa930d81 / #11050, and the test was subsequently moved to minSdk 24 without revisiting the skip.

I reconstructed the pre-#11050 runtime by removing only TrimmableTypeMapTypeManager.GetStaticMethodFallbackTypesCore() in the child overlay. Both trimmable CoreCLR/D8 and NativeAOT/R8 then failed with:

java.lang.NoSuchMethodError: no static method "Lexample/InterfaceMethods;.getStaticValue()I"

In both failures, DEX contained Lexample/InterfaceMethods$-CC;.getStaticValue()I. Restoring current production made every case pass, so the skip is stale and no new runtime fix is warranted.

The DEX assertions distinguish native interface methods at minSdk 24 from $-CC companions forced with a test-only minSdk 21 rewrite after product validation. Classfile inspection also confirms the covariant Object method is ACC_BRIDGE, ACC_SYNTHETIC.

Two independent gaps are kept outside this PR:

  • With forced desugaring, R8 removes static interface methods reachable only from managed JNI even on llvm-ir, producing the same error. That retention problem belongs to [TrimmableTypeMap] R8 shrinking removes unreferenced AndroidJavaSource (user Java) classes under NativeAOT #11774; the fixture keeps a Java call site so it tests runtime parity instead.
  • Directly binding the covariant default-interface implementation currently generates invalid C# before runtime (missing the base-return member and an invalid string-to-IJavaObject conversion). The bridge remains in the fixture as unbound Java and is executed through JNI.

Validation

  • 10/10 InterfaceMethods_* device cases on API 35 arm64 (emulator-5554):
    • llvm-ir/CoreCLR: native and desugared, D8 and R8
    • trimmable/CoreCLR: native and desugared, D8 and R8
    • trimmable/NativeAOT: native and desugared, R8
  • 20/20 DefaultInterfaceMethodsTests
  • 2/2 BindDefaultInterfaceMethods
  • git diff --check

Base: e534500b379a9e394745a2b303a079f42e548de7
Head: fb1f04af53393541e3b8cfff81fd97300daeb933

Replace the stale NativeAOT skip with API-native and forced-desugaring device coverage for static, default, nested, and covariant bridge interface methods across the llvm-ir and trimmable runtime matrix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 1, 2026 03:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
Severity Finding
Low severity tests/​MSBuildDeviceIntegration/​Tests/​InstallAndRunTests.cs — 💡 suggestion (performance)AssertInterfaceMethodDexShape calls DexUtils.ContainsClass*
Medium severity tests/​MSBuildDeviceIntegration/​Resources/​InterfaceMethodBridgeInvoker.java⚠️ warning (maintainability)invokeStaticMethods() is currently never invoked, so it…
What changed in this PR

Updates MSBuild device integration coverage to deterministically validate Java 8+ interface method desugaring behavior (native vs companion-class forms) across typemap implementations and runtimes, replacing a stale NativeAOT skip with explicit on-device assertions.

Changes:

  • Replaces SupportDesugaringStaticInterfaceMethods with a parameterized InterfaceMethodsMatchDesugaring matrix (llvm-ir/trimmable × D8/R8 × native/desugared, plus NativeAOT/R8).
  • Adds a richer Java fixture (static/default methods, nested interface, concrete default override, covariant bridge) and asserts both DEX shape and runtime execution.
  • Updates embedded resources and ResourceData plumbing to load the new Java sources.
File Description
tests/​MSBuildDeviceIntegration/​Tests/​InstallAndRunTests.cs Reworks the test into a matrix, injects runtime calls, and adds DEX-shape assertions for native vs desugared forms.
tests/​MSBuildDeviceIntegration/​Resources/​StaticMethodsInterface.java Removes the older, single static-method fixture.
tests/​MSBuildDeviceIntegration/​Resources/​InterfaceMethods.java New interface fixture with static + default methods and a nested interface.
tests/​MSBuildDeviceIntegration/​Resources/​InterfaceMethodPeer.java New peer type implementing both the main and nested interfaces.
tests/​MSBuildDeviceIntegration/​Resources/​ConcreteInterfaceMethodPeer.java Adds a concrete override calling InterfaceMethods.super to exercise default dispatch.
tests/​MSBuildDeviceIntegration/​Resources/​CovariantInterfaceMethods.java Adds a covariant default-interface method to produce/validate a bridge method shape.
tests/​MSBuildDeviceIntegration/​Resources/​InterfaceMethodBridgeInvoker.java Adds a JNI-invoked bridge runner and (intended) Java-side call sites for retention/parity testing.
tests/​MSBuildDeviceIntegration/​MSBuildDeviceIntegration.csproj Updates embedded resource glob to include the new interface-method Java fixtures.
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Utilities/​ResourceData.cs Adds new resource accessors for the added Java fixtures.

Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs Outdated
Track the exact normalized package for each matrix case and assert force-stop/uninstall cleanup before and after execution, including failure paths.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
simonrozsival and others added 3 commits September 1, 2026 12:57
Capture dexdump output once per matrix case and execute the Java static-method retention call sites as part of the observed bridge result.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Match class, method, and signature fields within one dexdump method record, keep stderr out of the parsed output, and cover overload and false-positive cases.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Require method signatures to immediately follow matching names, pass DEX filenames through ArgumentList, and cover malformed records, repeated classes, and CRLF output.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12610

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes

Findings: 0 errors · 1 warning · 1 suggestion

The desugaring matrix is well scoped: it verifies native and companion-class DEX forms across D8/R8 and both runtime typemap paths, while the runtime assertion covers the generated static/default/nested and covariant bridge calls. The new single-capture dexdump path also avoids repeatedly spawning the tool.

The cleanup invoked from finally should not be able to mask the primary test failure, and the direct adb invocation should use the repository's structured-argument pattern. Azure DevOps build 1576513 is still in progress; the checks reported so far have no failures.

Generated by Android PR Reviewer for #12610 · gpt56 · 209.7 AIC · ⌖ 9.1 AIC · ⊞ 25.7K
Comment /review to run again

Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs Outdated
Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs Outdated
Run cleanup adb commands with structured arguments and make final cleanup best-effort so it cannot replace a build, DEX, or runtime assertion failure.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12610

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes

Findings: 0 errors, 1 warning, 0 suggestions.

The interface-method matrix is comprehensive, the cached DEX parsing is covered by focused regression tests, and the test-only minSdk rewrite cleanly separates native and desugared shapes. One cleanup path still needs adjustment so cleanup failures are suppressed only when preserving an earlier test failure, not after a successful test body.

CI build 1576617 is still in progress with no failures reported at review time; CLA and Android Tools Tests Mac have passed.

Generated by Android PR Reviewer for #12610 · gpt56 · 169.9 AIC · ⌖ 8.86 AIC · ⊞ 25.7K
Comment /review to run again

"Managed and Java static, default, nested, and covariant bridge interface methods should all execute."
);
} finally {
TryCleanupInterfaceMethodPackage (proj.PackageName);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 ⚠️ Error handling — This unconditional best-effort cleanup also swallows an uninstall/query failure when the test body completed successfully, so a green case can leave the package installed and contaminate the device for later tests. Please only suppress cleanup exceptions while a primary test failure is already in flight; after a successful body, run the strict cleanup so its failure is reported (for example, track a completion flag and choose the cleanup path in finally).

Rule: Challenge exception swallowing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants